import 'dart:async';
import 'dart:convert';

import 'package:azlistview/azlistview.dart';
import 'package:flutter/material.dart';
import 'package:flutter_im_demo/db/db_help.dart';
import 'package:flutter_im_demo/http/dio.dart';
import 'package:flutter_im_demo/model/add_friend_message_model.dart';
import 'package:flutter_im_demo/model/chat_model.dart';
import 'package:flutter_im_demo/model/file_model.dart';
import 'package:flutter_im_demo/model/friend_model.dart';
import 'package:flutter_im_demo/model/message_model.dart';
import 'package:flutter_im_demo/model/result_model.dart';
import 'package:flutter_im_demo/model/user_info_model.dart';
import 'package:flutter_im_demo/socket/frame_parser.dart';
import 'package:flutter_im_demo/socket/socket_client.dart';
import 'package:uuid/uuid.dart';

import '../utils.dart';

class GlobalState with ChangeNotifier {
  static const _TAG = "[Global.State] ";

  bool isLogin;

  bool isLoading = true;

  UserInfoModel userInfoModel;

  List<Friend> friendList;

  Friend _friend; // 当前是否有聊天窗口

  final List<Message> _list = []; // 当前窗口的聊天记录

  final List<ChatModel> _chatModelList = []; // 最近聊天人

  GlobalState() {
    _getUserInfo();
  }

  List<ChatModel> get chatModelList => _chatModelList;

  int chatModelListLen() {
    return _chatModelList.length;
  }

  ChatModel getChatModel(int index) {
    return _chatModelList[index];
  }

  String _uid;

  String get uid => _uid;

  Friend getFriend() {
    return _friend;
  }

  set uid(String value) {
    _uid = value;
  }

  void setFriend(Friend friend) {
    _friend = friend;
    if (_friend != null) {
      printLog("当前朋友： ${friend.id}, ${friend.nickName}");
    } else {
      printLog("触发返回，清空消息列表");
      _list.clear();
      resetQueryMessageState();
    }
  }

  String errorMsg; // 全局错误信息

  Message getMessage(int index) => _list[index];

  List<String> getImageUrlList() {
    return _list
        .where((m) => 2 == m.type)
        .map((m) => m.fileModel != null ? m.fileModel.url : m.body)
        .toList();
  }

  int messageListLen() {
    return _list.length;
  }

  Future<void> addFileMessage(Message m) async {
    if (_friend != null) {
      _list.insert(0, m);
    }
    await _updateChatModelList(m, _friend); // 更新最近聊天人的消息
    notifyListeners();
  }

  Future<void> addMessage(Message m) async {
    await _insertMessage(m);
    if (_friend != null) {
      _list.insert(0, m);
    }
    await _updateChatModelList(m, _friend); // 更新最近聊天人的消息
    notifyListeners();
  }

  Future<bool> login(String phone, String password) async {
    bool _isLogin = isLogin;
    printLog("$phone 新用户登录");
    try {
      final response = await dio.post("/$apiVersion/login",
          data: {"phone": phone, "password": password});
      final respMap = response.data as Map;
      printLog(respMap.toString());
      if (respMap["code"] == 1) {
        final dataJson = respMap["data"];
        await _insertUser(dataJson);
        _insertFriendList(dataJson["friendList"]);
        _insertOfflineMessages(dataJson["offlineMessage"]);
        isLogin = true;
        _isLogin = true;

        if (logoutFlag) {
          printLog("退出后重新登录");
          await socketClient.init();
          _sendHeartbeatMessage0();
          logoutFlag = !logoutFlag;
        }
        socketClient.messageHandler = _messageHandler;
        socketClient.periodicHandler = _sendHeartbeatMessage;
      } else {
        errorMsg = respMap["errorMsg"];
      }
    } catch (e) {
      printLog("login error $e");
      errorMsg = "请检查网络";
    }

    notifyListeners();
    return _isLogin;
  }

  Future<void> _messageHandler(String msg) async {
    printLog("${userInfoModel.nickName} 收到消息: $msg");
    // 说明当前页面在聊天窗口

    Message m = Message.fromJson(json.decode(msg));
    printLog("_messageHandler: $m");
    switch (m.type) {
      case -2:
      case -1:
      case 0:
      case 1:
      case 2:
        m.combKey = m.toId + m.fromId;
        m.status = 1;
        m.readFlag = 0;
        await addMessage(m);
        break;

      case 3:
      case 4:
        // todo 处理加好友消息
        addFriendMessage = true;
        await insertAddFriendMsg(m);
        notifyListeners();
    }
  }

  // 是否有添加好友消息
  bool addFriendMessage = false;

  Future<void> _getUserInfo() async {
    final db = await dbHelp.database;
    final map = await db.rawQuery(UserInfoModel.findAllSQL);
    if (map.isEmpty) {
      isLogin = false;
      printLog("本地无用户信息");
    } else {
      printLog("本地存在用户信息直接登录");
      userInfoModel = UserInfoModel.fromJson(map[0]);
      socketClient.messageHandler = _messageHandler;
      socketClient.periodicHandler = _sendHeartbeatMessage;
      socketClient.sendHeartbeatMessage = _sendHeartbeatMessage0;
      friendList = await _queryFriendList();
      final chatList = await _queryChatModelList(friendList);
      if (chatList.isNotEmpty) {
        _chatModelList.addAll(chatList);
      }
      await _queryOfflineMessage();
      queryAddFriendMessage();
      countAddFriendMessage();
      isLogin = true;
      isLoading = false;
    }
    notifyListeners();
  }

  ResultModel<bool> sendMsg(Message message) {
    printLog(message.toString());
    final socket = socketClient.socket;
    if (socket != null) {
      String s = "${stringToBase64(message.toJson())}$messageEnd";
      try {
        socket.write(s);
//        socket.flush();
        return ResultModel<bool>(success: true, result: true);
      } catch (e) {
        print(e);
        printLog("send message error, $e");
        return ResultModel(fail: true, errorMsg: "请检查网络");
      }
    } else {
      printLog("send message error socket is null");
      return ResultModel(fail: true, errorMsg: "请检查网络");
    }
  }

  Timer _sendHeartbeatTimer = null;

  // 定时发送心跳信息
  void _sendHeartbeatMessage() {
    if (_sendHeartbeatTimer == null) {
      _sendHeartbeatMessage0();
      _sendHeartbeatTimer = Timer.periodic(Duration(minutes: 10), (t) {
        print('定时发送心跳消息');
        _sendHeartbeatMessage0();
      });
    }
  }

  void _sendHeartbeatMessage0() {
    if (userInfoModel == null) return;
    printLog("发送心跳信息");
    Message message = Message(
        type: -1,
        mid: Uuid().v4().toString().replaceAll("-", ""),
        fromId: userInfoModel.id);
    sendMsg(message);
  }

  Future<void> _insertUser(userData) async {
    userInfoModel = UserInfoModel.fromJson(userData);
    final db = await dbHelp.database;
    await db.rawInsert(UserInfoModel.insertSQL, [
      userInfoModel.id,
      userInfoModel.avatar,
      userInfoModel.phone,
      userInfoModel.nickName,
      userInfoModel.note,
      userInfoModel.city,
      userInfoModel.token,
      userInfoModel.expirationTime.millisecondsSinceEpoch ~/ 1000
    ]);
  }

  Future<void> _insertFriendList(friendListJson) async {
    friendList = Friend.fromJsonArray(friendListJson as List);
    //根据A-Z排序
    SuspensionUtil.sortListBySuspensionTag(friendList);
    final db = await dbHelp.database;
    for (var f in friendList) {
      final list =
          await db.rawQuery(Message.querySQL, [userInfoModel.id + f.id, 0, 1]);
      final msgList = Message.fromJsonList(list);
      final args = [
        f.id,
        f.avatar,
        f.nickName,
        f.note,
        f.city,
        msgList.isEmpty ? "" : msgList[0].mid
      ];
      printLog("FriendSQL: ${Friend.insertSQL}");
      printLog("args: ${args}");
      await db.rawInsert(Friend.insertSQL, args);
    }
    isLoading = false;
    notifyListeners();
  }

  Future<List<Friend>> _queryFriendList() async {
    final db = await dbHelp.database;
    List<Map<String, dynamic>> list = await db.rawQuery(Friend.queryAllSQL);
    return Friend.fromJsonArray(list);
  }

  static printLog(String l) {
    print("$_TAG-$l");
  }

  Future<void> _insertMessage(Message message) async {
    Friend friend = _friend;
    if (friend == null && message.fromId != userInfoModel.id) {
      friend = await _queryFriendById(message.fromId);
    }
    final db = await dbHelp.database;
    printLog(Message.insertSQL);
    printLog("${message.getInsertArguments()}");
    final f1 = db.rawInsert(Message.insertSQL, message.getInsertArguments());
    final f2 = db.rawInsert(Friend.updateLastMsgSQL, [message.mid, friend.id]);
    await Future.wait([f1, f2]);
    printLog("_insertMessage ${message}");
  }

  Future<void> dispose() async {
    final db = await dbHelp.database;
    await db.close();
    _sendHeartbeatTimer.cancel();
    socketClient.close();
    super.dispose();
  }

  bool queryMessageLoading = false;
  int _start = 0;
  int _length = 10;
  bool moreFlag = true; // 还有更多

  Future<bool> queryHistoryMessage() async {
    queryMessageLoading = true;
    List<Message> list = await _queryHistoryMessage0();
    printLog("queryHistoryMessage list len: ${list.length}");
    _list.addAll(list);
    queryMessageLoading = false;
    notifyListeners();
    return moreFlag;
  }

  Future<List<Message>> _queryHistoryMessage0() async {
    final db = await dbHelp.database;
    final combKey = userInfoModel.id + _friend.id;
    printLog("combKey: ${combKey}");
    final args = [combKey, _start, _length];
    final list = await db.rawQuery(Message.querySQL, args);
    printLog("sqlArgs: startNum: ${args[1]} len: ${args[2]}");
    if (list.length < _length) {
      moreFlag = false;
    } else {
      _start += _length;
    }
    List<Message> list0 = Message.fromJsonList(list);
    final futureList = list0.map((m) async {
      if (m.fileType()) {
        final list = await db.rawQuery(FileModel.queryByMid, [m.mid]);
        if (list.isNotEmpty) {
          m.fileModel = FileModel.fromJson(list[0]);
        }
      }
      return m;
    });
    return await Future.wait(futureList);
  }

  // 重置 查询时候的临时状态
  void resetQueryMessageState() {
    _start = 0;
    _length = 10;
    moreFlag = true;
  }

  Future<List<ChatModel>> _queryChatModelList(List<Friend> friendList) async {
    final db = await dbHelp.database;
    final list = friendList
        .where((f) => f.lastMsgId.isNotEmpty)
        .map((f) async {
          final m = await db.rawQuery(Message.queryById, [f.lastMsgId]);
          return m.isNotEmpty
              ? ChatModel(friend: f, message: Message.fromJson(m[0]))
              : null;
        })
        .where((f) => f != null)
        .toList();

    final result =
        await Future.wait(list); // Cannot modify an unmodifiable list

    final newList = result.where((i) => i != null).toList();
    newList.sort((c1, c2) => c2.message.time.compareTo(c1.message.time));
    return newList;
  }

  Future<void> _updateChatModelList(Message m, Friend f) async {
    printLog("_updateChatModelList : ${f?.toString()}, msg: ${m}");
    if (f == null && m.fromId != userInfoModel.id) {
      f = await _queryFriendById(m.fromId);
    }
    int index = _chatModelList.indexWhere((c) => c.friend.id == f.id);
    bool newMessageFlag =
        (_friend == null ? true : (f.id == _friend.id ? false : true));
    final chatModel =
        ChatModel(friend: f, message: m, newMessage: newMessageFlag);
    if (index != -1) {
      // 找到则替换
      _chatModelList[index] = chatModel;
    } else {
      // 没有找到则添加
      _chatModelList.add(chatModel);
    }
    for (var i = 0; i < _chatModelList.length; i++) {
      ChatModel chatModel = _chatModelList[i];
      if (chatModel.friend.id == f.id) {
        chatModel.message = m;
        break;
      }
    }

    _chatModelList.sort((c1, c2) => c2.message.time.compareTo(c1.message.time));
  }

  void updateRemoveNewMessageFlag(Friend friend) {
    if (_chatModelList.isEmpty) return;

    int index = _chatModelList.indexWhere((f) => f.friend.id == friend.id);
    if (index != -1) {
      final model = _chatModelList[index];
      if (model.newMessage) {
        model.newMessage = false;
        notifyListeners();
      }
    }
  }

  Future<Friend> _queryFriendById(String fromId) async {
    final db = await dbHelp.database;
    final list = await db.rawQuery(Friend.queryById, [fromId]);
    return Friend.fromJson(list[0]);
  }

  bool logoutFlag = false;

  Future<void> logout() async {
    printLog("退出应用");
    logoutFlag = true;
    final db = await dbHelp.database;
    await Future.wait([
      db.rawDelete(UserInfoModel.deleteSQL, [userInfoModel.id]),
      db.rawDelete(Friend.deleteSQL)
    ]);
    isLogin = false;
    isLoading = true;
    userInfoModel = null;
    friendList = null;
    _chatModelList.clear();
    _uid = null;
    _sendHeartbeatTimer?.cancel();
    _sendHeartbeatTimer = null;
    socketClient.messageHandler = null;
    socketClient.periodicHandler = null;
    socketClient.sendHeartbeatMessage = null;
    socketClient.close();
  }

  /// {
  //  "d2ac93383c8547b3ad79f7d661cd28d2" : [ {
  //    "mid" : "6b8293c82ab64def883252252b639966",
  //    "fromId" : "d2ac93383c8547b3ad79f7d661cd28d2",
  //    "toId" : "db0e1e0cb5f14618a87d84e695943498",
  //    "body" : "恶魔",
  //    "time" : 1566549785472,
  //    "type" : 0
  //  } ]
  //}
  Future<void> _insertOfflineMessages(Map<String, dynamic> respMap) async {
    if (respMap == null || respMap.isEmpty) return;
    printLog("保存离线信息 _insertOfflineMessages");

    final db = await dbHelp.database;
    for (var entry in respMap.entries) {
      final k = entry.key;
      final v = entry.value;
      final list = v as List;
      List<Message> msgList =
          list.map((d) => Message.fromJson(d as Map<String, dynamic>)).toList();
      final futureList = msgList.map((mItem) async {
        if (mItem.type != 4) {
          mItem.combKey = userInfoModel.id + k;
        }
        await db.rawInsert(Message.insertSQL, mItem.getInsertArguments());
        return mItem;
      });
      List<Message> result = await Future.wait(futureList);
      printLog("$k :: 离线消息写入成功");

      final friendMap = await db.rawQuery(Friend.queryById, [k]);
      final friend = Friend.fromJson(friendMap[0]);
      int index = _chatModelList.indexWhere((c) => c.friend.id == friend.id);
      if (index != -1) {
        if (result.last.type != 4) {
          _chatModelList[index].message = result.last;
          _chatModelList[index].newMessage = true;
        }
      } else {
        if(result.last.type != 4) {
          _chatModelList.insert(0,
              ChatModel(
                  friend: friend, message: result.last, newMessage: true));
        }
      }
      notifyListeners();
    }
  }

  Future<void> updateMessageToImage(String url, Message msg) async {
    int index = _list.indexWhere((m) => m.mid == msg.mid);
    final db = await dbHelp.database;
    if (index != -1) {
      Message msg = _list[index];
      msg.fileModel.url = url;
      print("updateMessageToImage: ${msg}");
      notifyListeners();
      await db.rawUpdate(FileModel.updateSQL, [url, msg.fileModel.id]);
    }
    Message msg0 = msg.convertToImageMsg();
    printLog("imageMessage: $msg0");
    sendMsg(msg0);
  }

  Future<void> _queryOfflineMessage() async {
    printLog("_queryOfflineMessage");
    final response = await dio.post("/$apiVersion/queryOfflineMessage",
        data: {"id": userInfoModel.id});
    printLog("resp: $response");
    final respMap = response.data as Map;
    int code = respMap["code"];
    if (code == 1) {
      await _insertOfflineMessages(respMap["data"]);
    } else {
      errorMsg = respMap["errorMsg"];
    }
  }

  // 查询有没有添加好友消息
  static Future<List<AddFriendMessageModel>> queryAddFriendMessage() async {
    final db = await dbHelp.database;
    final list = await db.rawQuery(Message.queryAddFriendMsgSQL, [0, 20]);
    final msgList = Message.fromJsonList(list).map((value) {
      final body = json.decode(value.body);
      return AddFriendMessageModel(
          friendId: value.fromId,
          avatar: body["avatar"],
          nickName: body["nickName"],
          body: body["body"],
          msgId: value.mid);
    }).toList();
    printLog("msgList: " + msgList.toString());
    return msgList;
  }

  Future<void> countAddFriendMessage() async {
    final db = await dbHelp.database;
    final countList = await db.rawQuery(Message.countAddFriendMsgSQL);
    printLog("countList: " + countList.toString());
    if (countList[0]["count"] > 0) {
      printLog("存在添加好友消息：true");
      addFriendMessage = true;
    } else {
      addFriendMessage = false;
    }
    notifyListeners();
  }

  void updateAddFriendMessageFlag(bool flag) {
    addFriendMessage = flag;
    notifyListeners();
  }

  Future<void> insertAddFriendMsg(Message msg) async {
    printLog("insertAddFriendMsg: ${msg.toString()}");
    final db = await dbHelp.database;
    await db.rawInsert(Message.insertSQL, msg.getInsertArguments());
    printLog("写入消息成功");
  }
}
